package sample.classremaper;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.StaticInitMerger;
import org.objectweb.asm.tree.ClassNode;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class ClassMergerVisitor extends StaticInitMerger {
    private final ClassNode anotherClass;
    private final List<String> methodNames = Lists.newArrayList();
    private final List<String> fieldNames = Lists.newArrayList();

    public ClassMergerVisitor(int api, String prefix, ClassVisitor classVisitor, ClassNode anotherClass) {
        super(api, prefix, classVisitor);
        this.anotherClass = anotherClass;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        fieldNames.add(name);
        return super.visitField(access, name, descriptor, signature, value);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        methodNames.add(name + descriptor);
        return super.visitMethod(access, name, descriptor, signature, exceptions);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        HashSet<String> interfaceSet = new HashSet<>(Arrays.asList(interfaces));
        interfaceSet.addAll(anotherClass.interfaces);
        super.visit(version, access, name, signature, superName, interfaceSet.toArray(new String[0]));
    }


    @Override
    public void visitEnd() {
        anotherClass.fields.forEach(fieldNode -> {
            if (!fieldNames.contains(fieldNode.name)) {
                fieldNode.accept(this);
            }
        });
        anotherClass.methods.forEach(methodNode -> {
            if (!methodNames.contains(methodNode.name + methodNode.desc)
                    && !StringUtils.contains(methodNode.name, "<init>")) {
                methodNode.accept(this);
            }
        });
        super.visitEnd();
    }
}
